#ifndef __FPGA_SIM_API_H__
#define __FPGA_SIM_API_H__

#include <cstdint>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <stdint.h>
#include <unistd.h>
#include <vector>

using namespace std;

typedef enum ck_md_algo_ {
  CK_MD_SHA1 = 0,
  CK_MD_SHA256 = 1,
  CK_MD_SHA384 = 2,
  CK_MD_SM3 = 4,
  CK_MD_MD5 = 5,
} ck_md_algo_e;

typedef struct {
  uint8_t *allocated;
  uint8_t *aligned;
  intptr_t offset;
  intptr_t sizes;
  intptr_t strides;
} MemRef1D;

// define
constexpr const char *SHA1_SIM_PATH = "SHA1_FPGA_SIM_PATH";
constexpr const char *SHA1_SIM_TMP_FILE = "/VCS/template.v";
constexpr const char *SHA1_SIM_SRC_FILE = "/SRC/*.v";
constexpr const char *SHA1_SIM_O_FILE = "/VCS/testbench.v";

// MD5 SIM PATH
constexpr const char *MD5_SIM_PATH = "MD5_FPGA_SIM_PATH";
constexpr const char *MD5_SIM_TMP_FILE = "/VCS/template.v";
constexpr const char *MD5_SIM_SRC_FILE = "/SRC/*.v";
constexpr const char *MD5_SIM_O_FILE = "/VCS/testbench.v";

// SM3 SIM PATH
constexpr const char *SM3_SIM_PATH = "SM3_FPGA_SIM_PATH";
constexpr const char *SM3_SIM_TMP_FILE = "/VCS/template.v";
constexpr const char *SM3_SIM_SRC_FILE = "/SRC/*.v";
constexpr const char *SM3_SIM_O_FILE = "/VCS/testbench.v";

// AES SIM PATH
constexpr const char *AES_SIM_PATH = "AES_FPGA_SIM_PATH";
constexpr const char *AES_CRACK_SIM_PATH = "AES_FPGA_CRACK_SIM_PATH";
constexpr const char *AES_SIM_TMP_FILE = "/VCS/template.v";
constexpr const char *AES_SIM_SRC_FILE = "/SRC/*.v";
constexpr const char *AES_SIM_O_FILE = "/VCS/testbench.v";

// SHA-1 Interface
bool CreateSimFile(const string &sim_template_path,
                   const string &output_file_path,
                   const vector<unsigned char> &data, vector<uint32_t> &result);
bool GetResultFromVCS(const string &src_files_path, const string &sim_file_path,
                      vector<uint32_t> &result);
vector<unsigned char> sha1_padding(const vector<unsigned char> &input_data);
bool sha1_padded(const string &sim_template_path,
                 const string &output_file_path, const string &src_files_path,
                 const vector<unsigned char> &input_data,
                 vector<uint32_t> &result);
void computeSha1(const string &sim_template_path,
                 const string &output_file_path, const string &src_files_path,
                 const vector<unsigned char> &input_data, uint8_t *cipher_addr);
void fpga_sha1(uint8_t *plain_addr, uint32_t plain_len, uint8_t *cipher_addr);

// MD5 Interface
bool CreateMd5SimFile(const string &sim_template_path,
                      const string &output_file_path,
                      const vector<unsigned char> &data,
                      vector<uint32_t> &result);
bool GetMd5ResultFromVCS(const string &src_files_path,
                         const string &sim_file_path, vector<uint32_t> &result);
vector<unsigned char> md5_padding(const vector<unsigned char> &input_data);
bool md5_padded(const string &sim_template_path, const string &output_file_path,
                const string &src_files_path,
                const vector<unsigned char> &input_data,
                vector<uint32_t> &result);
void computeMd5(const string &sim_template_path, const string &output_file_path,
                const string &src_files_path,
                const vector<unsigned char> &input_data, uint8_t *cipher_addr);
void fpga_md5(uint8_t *plain_addr, uint32_t plain_len, uint8_t *cipher_addr);

// SM3 Interface
bool CreateSm3SimFile(const string &sim_template_path,
                      const string &output_file_path,
                      const vector<unsigned char> &data,
                      vector<uint32_t> &result);
bool GetSm3ResultFromVCS(const string &src_files_path,
                         const string &sim_file_path, vector<uint32_t> &result);
vector<unsigned char> sm3_padding(const vector<unsigned char> &input_data);
bool sm3_padded(const string &sim_template_path, const string &output_file_path,
                const string &src_files_path,
                const vector<unsigned char> &input_data,
                vector<uint32_t> &result);
void computeSm3(const string &sim_template_path, const string &output_file_path,
                const string &src_files_path,
                const vector<unsigned char> &input_data, uint8_t *cipher_addr);
void fpga_sm3(uint8_t *plain_addr, uint32_t plain_len, uint8_t *cipher_addr);

// AES Interface
void fpga_aes128(uint8_t *plain_addr, uint32_t plain_len, uint8_t *key,
                 uint8_t *cipher_addr);
void fpga_aes128Crack(uint8_t *plain_addr, uint32_t plain_len, uint8_t *key,
                      uint8_t *cipher_addr);
void computeAes128(const string &sim_template_path,
                   const string &output_file_path, const string &src_files_path,
                   const vector<unsigned char> &input_data, uint8_t *key,
                   uint8_t *cipher_addr);
vector<unsigned char> aes_padding(const vector<unsigned char> &input_data);
bool aes_padded(const string &sim_template_path, const string &output_file_path,
                const string &src_files_path,
                const vector<unsigned char> &input_data,
                const vector<unsigned char> &key, vector<uint32_t> &result);
bool GetAesResultFromVCS(const string &src_files_path,
                         const string &sim_file_path, vector<uint32_t> &result);
bool CreateAesSimFile(const string &sim_template_path,
                      const string &output_file_path,
                      const vector<unsigned char> &data,
                      const vector<unsigned char> &key,
                      vector<uint32_t> &result);

extern "C" {
// TODO: access FPGA algo_run
void algo_run(char *algo_name, uint8_t *plain_addr, uint32_t plain_len,
              uint8_t *cipher_addr);

void algo_aes_run(char *algo_name, uint8_t *plain_addr, uint32_t plain_len,
                  uint8_t *cipher_addr, uint8_t *key);

void sha1_run(uint8_t *input_allocated, uint8_t *input_aligned,
              intptr_t input_offset, intptr_t input_size, intptr_t input_stride,
              uint32_t plain_len, uint8_t *output_allocated,
              uint8_t *output_aligned, intptr_t output_offset,
              intptr_t output_size, intptr_t output_stride);

MemRef1D sha1Crack_run(uint8_t *hash_allocated, uint8_t *hash_aligned,
                       intptr_t hash_offset, intptr_t hash_size,
                       intptr_t hash_stride, uint8_t *input_allocated,
                       uint8_t *input_aligned, intptr_t input_offset,
                       intptr_t input_size, intptr_t input_stride,
                       uint32_t msg_num, uint32_t msg_len);

void md5_run(uint8_t *input_allocated, uint8_t *input_aligned,
             intptr_t input_offset, intptr_t input_size, intptr_t input_stride,
             uint32_t plain_len, uint8_t *output_allocated,
             uint8_t *output_aligned, intptr_t output_offset,
             intptr_t output_size, intptr_t output_stride);

MemRef1D md5Crack_run(uint8_t *hash_allocated, uint8_t *hash_aligned,
                      intptr_t hash_offset, intptr_t hash_size,
                      intptr_t hash_stride, uint8_t *input_allocated,
                      uint8_t *input_aligned, intptr_t input_offset,
                      intptr_t input_size, intptr_t input_stride,
                      uint32_t msg_num, uint32_t msg_len);

void sm3_run(uint8_t *input_allocated, uint8_t *input_aligned,
             intptr_t input_offset, intptr_t input_size, intptr_t input_stride,
             uint32_t plain_len, uint8_t *output_allocated,
             uint8_t *output_aligned, intptr_t output_offset,
             intptr_t output_size, intptr_t output_stride);

MemRef1D sm3Crack_run(uint8_t *hash_allocated, uint8_t *hash_aligned,
                      intptr_t hash_offset, intptr_t hash_size,
                      intptr_t hash_stride, uint8_t *input_allocated,
                      uint8_t *input_aligned, intptr_t input_offset,
                      intptr_t input_size, intptr_t input_stride,
                      uint32_t msg_num, uint32_t msg_len);

void aes_run(uint8_t *input_allocated, uint8_t *input_aligned,
             intptr_t input_offset, intptr_t input_size, intptr_t input_stride,
             uint32_t plain_len, uint8_t *output_allocated,
             uint8_t *output_aligned, intptr_t output_affset,
             intptr_t output_size, intptr_t output_stride,
             uint8_t *key_allocated, uint8_t *key_aligned, intptr_t key_offset,
             intptr_t key_size, intptr_t key_stride,
             uint8_t *sBoxBase_allocated, uint8_t *sBoxBase_aligned,
             intptr_t sBoxBase_offset, intptr_t sBoxBase_size,
             intptr_t sBoxBase_stride);

void aesCrack_run(uint8_t *input_allocated, uint8_t *input_aligned,
                  intptr_t input_offset, intptr_t input_size,
                  intptr_t input_stride, uint32_t plain_len,
                  uint8_t *output_allocated, uint8_t *output_aligned,
                  intptr_t output_affset, intptr_t output_size,
                  intptr_t output_stride, uint32_t cptLen,
                  uint8_t *key_allocated, uint8_t *key_aligned,
                  intptr_t key_offset, intptr_t key_size, intptr_t key_stride,
                  uint32_t keyLen, uint32_t msgCnt);
}

#endif